package org.example.physics_engine.collision.contact;


import javafx.util.Pair;
import lombok.AllArgsConstructor;
import lombok.Data;
import org.example.physics_engine.collision.manifold.Manifold;
import org.example.physics_engine.collision.manifold.ManifoldPoint;
import org.example.physics_engine.body.PhysicsBody;
import org.example.physics_engine.geometry.Vector2d;

import java.util.ArrayList;
import java.util.List;

//代表每一对碰撞的PhysicBody的碰撞点的约束
@Data
@AllArgsConstructor
public class ContactConstraint {
    //碰撞物体对
    private Pair<PhysicsBody, PhysicsBody> pair;

    //接触信息列表
    private List<SolvableContact> contacts;

    //碰撞法线
    private Vector2d normal;

    //碰撞切线
    private Vector2d tangent;

    //阻力系数
    private double friction;

    //回弹系数
    private double restitution;

    //回弹启动速度
    private double restitutionVelocity;

    //接触面切线速度（用于作用摩擦力）
    private double tangentSpeed;

    //接触信息列表大小
    private int size;

    public ContactConstraint(Pair<PhysicsBody, PhysicsBody> pair) {
        this.pair = pair;
        this.contacts = new ArrayList<>(2);
        this.normal = new Vector2d();
        this.tangent = new Vector2d();
        this.tangentSpeed = 0;
        this.size = 0;
    }

    //使用Manifold来更新本ContactConstraint
    public void update(Manifold manifold) {
        PhysicsBody body1 = getBody1();
        PhysicsBody body2 = getBody2();

        Vector2d normal = manifold.getNormal();
        this.normal.x = normal.x;
        this.normal.y = normal.y;

        this.tangent.x = normal.y;
        this.tangent.y = -normal.x;

        this.friction = this.getMixedFriction(body1, body2);
        this.restitution = this.getMixedRestitution(body1, body2);
        this.restitutionVelocity = this.getMixedRestitutionVelocity(body1, body2);

        this.tangentSpeed = 0;

        List<ManifoldPoint> points = manifold.getPoints();
        List<SolvableContact> contacts = new ArrayList<>(points.size());
        for (ManifoldPoint point : points) {
            SolvableContact newContact = new SolvableContact(
                    point.getPoint(),
                    point.getDepth(),
                    body1.getShape().getPoint(point.getPoint()),
                    body2.getShape().getPoint(point.getPoint()));
            contacts.add(newContact);
        }
        this.contacts.clear();
        this.contacts.addAll(contacts);
        this.size = this.contacts.size();
    }

    //返回碰撞对第一个
    public PhysicsBody getBody1() {
        return pair.getKey();
    }

    //返回碰撞对第二个
    public PhysicsBody getBody2() {
        return pair.getValue();
    }


    private double getMixedRestitutionVelocity(PhysicsBody body1, PhysicsBody body2) {
        return Math.max(body1.getRestitutionVelocity(), body2.getRestitutionVelocity());
    }

    private double getMixedRestitution(PhysicsBody body1, PhysicsBody body2) {
        return Math.max(body1.getRestitution(), body2.getRestitution());
    }

    private double getMixedFriction(PhysicsBody body1, PhysicsBody body2) {
        return Math.min(body1.getRestitution(), body2.getRestitution());
    }

}
